/**
 * SessionHelper.java
 * @author: liuex
 * 2008-1-7 ����09:09:50
 *
 * note: 
 */
package org.lex.app.sessions;

import java.awt.Component;
import java.awt.Frame;
import java.awt.Window;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;

import org.lex.app.prefs.ResourceMap;
import org.lex.awt.ComponentUtilities;
import org.lex.awt.ComponentVisitor;
import org.lex.utils.Codec;
import org.lex.utils.functor.UnaryFunctor;


public class SessionStorageHelper {
	private static final Map<Class<?>, Session> map;
	static {
		map = new Hashtable<Class<?>, Session>();
		registerSession(Window.class, new WindowSession());
		registerSession(Frame.class, new FrameSession());
		registerSession(JSplitPane.class, new JSplitPaneSession());
		registerSession(JTable.class, new JTableSession());
		registerSession(JTabbedPane.class, new JTabbedPaneSession());
		// ButtonSession bs = new ButtonSession();
		// registerSession( JToggleButton.class, bs );
		// registerSession( JCheckBoxMenuItem.class, bs );
		// registerSession( JRadioButtonMenuItem.class, bs );
	}

	public static <T extends Session> void registerSession(Class<? extends Component> clz, T session) {
		map.put(clz, session);
	}

	public static Set<Map.Entry<Class<?>, Session>> availableSessions() {
		return Collections.unmodifiableSet(map.entrySet());
	}

	public static void save(Component root, ResourceMap dest) {
		long start = System.currentTimeMillis();
		Iterator<Component> iter = ComponentVisitor.bfs(root);
		while (iter.hasNext()) {
			Component c = iter.next();
			Session ses = getSession(c);
			if (null != ses)
				saveComponent(c, ses, dest);
		}
		long time = System.currentTimeMillis() - start;
		System.out.println("saveing-session using: " + time + "ms");
	}

	public static void restore(Component root, final ResourceMap src) {
		long start = System.currentTimeMillis();
		restore1(root, src);
		long time = System.currentTimeMillis() - start;
		System.out.println("restore-session using: " + time + "ms");
	}

	static void restore1(Component root, final ResourceMap src) {
		ComponentVisitor.bfs(root, new UnaryFunctor<Void, Component>() {
			public Void operate(Component c) {
				Session ses = getSession(c);
				if (null != ses)
					restoreComponent(c, ses, src);
				return null;
			}
		});
	}

	static void restore2(Component root, final ResourceMap src) {
		Iterator<Component> iter = ComponentVisitor.bfs(root);
		while (iter.hasNext()) {
			Component c = iter.next();
			Session ses = getSession(c);
			if (null != ses)
				restoreComponent(c, ses, src);
		}
	}

	private static void saveComponent(Component c, Session session, ResourceMap dest) {
		// we put an extra item to save propertyNames in ResourceMap
		String name = getComponentName(c);
		session = session.getComponentProperties(c);
		String[] keys = session.propertyNames();
		dest.putString(name, Codec.encodeStringArray(keys));
		for (String key : keys)
			dest.putString(name + "." + key, session.getProperty(key));
	}

	private static void restoreComponent(Component c, Session session, ResourceMap src) {
		String name = getComponentName(c);
		String[] keys = Codec.decodeStringArray(src.getString(name, ""));
		for (String key : keys)
			if (false == key.isEmpty())
				session.putProperty(key, src.getString(name + "." + key));
		session.restoreComponent(c);
	}

	private static Session getSession(Component c) {
		if (null == c)
			return null;
		Class<?> clz = c.getClass();
		while (true) {
			Session session = map.get(clz);
			if (null != session) {
				if (false == notNull(c))
					return null;
				return session;
			}
			clz = clz.getSuperclass();
			if (null == clz)
				return null;
		}
	}

	private static String getComponentName(Component c) {
		return c.getName();
	}

	/**
	 * check 3 things:<br>
	 * 1. c == null<br>
	 * 2. c.getName() == null <br>
	 * 3. c.getName().trim().length() == 0 <br>
	 * return true if past all 3 tests;
	 */
	private static boolean notNull(Component c) {
		if (null == c) {
			error("component can't be null");
			return false;
		}
		String name = c.getName();
		if (null == name) {
			nullNameError(c, "component must have a non-null name for session-storage");
			return false;
		}
		name = name.trim();
		if (0 == name.length()) {
			nullNameError(c, "component name(or after trim()) should be a non-empty string");
			return false;
		}
		return true;
	}

	private static void nullNameError(Component c, String descrip) {
		error("Session-storage error on: " + c);
		error("Details: " + descrip);
		ComponentUtilities.dumpComponent(c, System.err);
	}

	private static void error(String msg) {
		System.err.println(msg);
	}

	private SessionStorageHelper() {
	}
}
